﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Reflection.PortableExecutable;

namespace SimpleMapTemplate
{
	/// <summary>
	/// 
	/// </summary>
	public static class DtoWrapper
	{

		/// <summary>
		/// 
		/// </summary>
		private static Dictionary<string, Type> _WrapperType = new Dictionary<string, Type>();
		/// <summary>
		/// 
		/// </summary>
		/// <param name="anonymous"></param>
		/// <returns></returns>
		public static Type CreateAnonymousType(object anonymous)
		{
			var type = anonymous.GetType();
			string classNameWapper = type.FullName + "Wrapper";

			if (_WrapperType.ContainsKey(classNameWapper))
			{
				return _WrapperType[classNameWapper];
			}
			var props = type.GetProperties()
							 .Select(f => new { PropName = f.Name, PropType = f.PropertyType })
							 .ToArray();

			var assemblyName = new AssemblyName("DynamicWapperAssembly");
			var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
			var moduleBuilder = assemblyBuilder.DefineDynamicModule("DtoWapperModule");
			var typeBuilder = moduleBuilder.DefineType(type.FullName + "Wapper", TypeAttributes.Class | TypeAttributes.Public, typeof(object));
			foreach (var prop in props)
			{
				DefineProperty(typeBuilder, prop.PropName, prop.PropType);
			}
			return typeBuilder.CreateType();
		}

		/// <summary>
		/// 动态创建属性字段
		/// get set 方法
		/// 可以用于生成wapper字段映射数据名称
		/// </summary>
		/// <param name="typeBuilder"></param>
		/// <param name="fieldBuilder"></param>
		/// <param name="name"></param>
		/// <param name="type"></param>
		public static void DefineProperty(TypeBuilder typeBuilder,string name,Type type)
		{
			var fieldBuilder = typeBuilder.DefineField("_" +name,type, FieldAttributes.Private);
			var propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None,type, null);
			var getIl = typeBuilder.DefineMethod("get_" + name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, type, Type.EmptyTypes);
			var getIlGenerator = getIl.GetILGenerator();
			getIlGenerator.Emit(OpCodes.Ldarg_0);
			getIlGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
			getIlGenerator.Emit(OpCodes.Ret);
			propertyBuilder.SetGetMethod(getIl);
			var setIl = typeBuilder.DefineMethod("set_" + name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] {type });
			var setIlGenerator = setIl.GetILGenerator();
			setIlGenerator.Emit(OpCodes.Ldarg_0);
			setIlGenerator.Emit(OpCodes.Ldarg_1);
			setIlGenerator.Emit(OpCodes.Stfld, fieldBuilder);
			setIlGenerator.Emit(OpCodes.Ret);
			propertyBuilder.SetSetMethod(setIl);

		}
	}
}
